<?php

/**
 * 
 * @desc 框架系统错误捕捉类
 * @author keluo<pycorvn@yeah.net>
 * @since 2015-01-24
 */
class SCError {

    public function __construct() {
        
    }

    /**
     * 
     * 系统错误处理
     * @param integer $code 错误状态码
     * @param string $message
     * @param string $file
     * @param integer $line
     * @param array $context
     */
    public static function errorHandler($code, $message, $file, $line, $context = null) {
        $trace = self::getDebugTraceString();
        self::showDebugPage(false, compact('code', 'message', 'file', 'line', 'trace'));
        return true;
    }

    /**
     * 异常错误处理
     * @param Exception $e
     */
    public static function exceptionHandler(Exception $e) {
        $log = array(
            'code' => $e->getCode(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'message' => $e->getMessage(),
            'trace' => self::getDebugTraceString()
        );
        self::showDebugPage(true, $log);
    }

    /**
     * 致命错误处理
     */
    public static function fatalHandler() {
        if (!is_null($e = error_get_last())) {
            $messages = explode(PHP_EOL, $e['message']);
            $message = array_shift($messages);
            if ($messages)
                unset($messages[0]);
            $log = array(
                'code' => $e['type'],
                'file' => $e['file'],
                'line' => $e['line'],
                'message' => $message,
                'trace' => implode(PHP_EOL, $messages)
            );
            self::showDebugPage(true, $log);
        }
    }

    public static function setError($msg) {
        SCScabish::core('session')->set('SC_EXCEPTION_MESSAGE', $msg);
    }

    public static function getError() {
        return SCScabish::core('session')->get('SC_EXCEPTION_MESSAGE');
    }

    /**
     * 显示debug页面
     * @param boolean $stop 是否停止执行
     * @param array $data debug信息
     */
    protected static function showDebugPage($stop = true, array $data) {
        if (SC_DEBUG) {
            $msg = '<meta http-equiv="content-type" content="text/html;charset=utf-8">';
            $msg .= '<style>.SCABISH{font-family: microsoft yahei,Monaco,Consolas;font-size:100%;margin:.2em;padding:.6em;clear:both;text-align:left;background:#f6f5ec;color:#000;}';
            $msg .= '.SCABISH p{margin:0;padding:0;line-height:1.2em;}';
            $msg .= '.SCABISH .trace{border-top:1px dashed silver;padding-top:5px;margin-top:5px;}';
            $msg .= '.SCABISH .homepage{text-align:right;}';
            $msg .= '.SCABISH .homepage a{font-size:.8em;color:#7bbfea;text-decoration:none;}</style>';
            $msg .= '<div class="SCABISH">';
            $msg .= '<p>异常代码：' . self::translate($data['code']) . '</p>';
            $msg .= '<p>异常信息：<span style="color:#f15a22;">' . $data['message'] . '</span></p>';
            $msg .= '<p>触发位置：' . $data['file'] . '(' . $data['line'] . ')</p>';
            $msg .= $data['trace'] ? '<div class="trace"><p>跟踪信息：</p>' . nl2br($data['trace']) . '</div>' : '';
            $msg .= '<p class="homepage"><a href="http://git.oschina.net/pycorvn/Scabish" target="blank">@Scabish Framework</a></p>';
            $msg .= '</div>';
            if ($stop) {
                header("HTTP/1.1 500 Internal Server Error");
                die($msg);
            } elseif (SC_DEBUG) {
                echo $msg;
            }
        } else {
            if (in_array($data['code'], array(E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE)))
                return;
            if (SCS::config()->error) {
                SCS::request()->route(SCS::config()->error);
            } else {
                header("HTTP/1.1 500 Internal Server Error");
                die($data['message']);
            }
        }
    }

    /**
     * 记录异常信息到日志
     * @param array $log
     */
    protected static function log(array $log) {
        if (false !== stripos($log['code'], 'is deprecated in PHP'))
            return;
        $log_path = rtrim(SCConfig::get('LOG_PATH'), '/');
        $log_path || $log_path = SC_LOG_PATH;
        $target_path = $log_path;
        mkdir($target_path, 0775, true);
        $target_file = $log_path . '/' . date('Ym') . '.log';
        if (file_exists($target_file) && is_file($target_file)) {
            
        } else {
            touch($target_file);
        }

        $msg = '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL;
        $msg .= '异常代码: ' . self::translate($log['code']) . PHP_EOL;
        $msg .= '异常信息: ' . $log['message'] . PHP_EOL;
        $msg .= '发生文件: ' . $log['file'] . '(' . $log['line'] . ')' . PHP_EOL;
        $msg .= '跟踪信息: ' . PHP_EOL . $log['trace'] . PHP_EOL;
        $msg .= str_pad('', 100, '-') . PHP_EOL;

        file_put_contents($target_file, $msg . file_get_contents($target_file));
    }

    /**
     * 错误代码翻译
     * @param integer $code
     * @return string
     */
    protected static function translate($code) {
        $map = array(
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            0 => 'E_USER_ERROR',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
        );
        return isset($map[$code]) ? $map[$code] : $code;
    }

    /**
     * 获取debug跟踪信息
     */
    protected static function getDebugTraceString() {
        $traces = debug_backtrace();
        $trace = $traces[1];
        if ($trace['function'] == 'exceptionHandler') {
            $args = $trace['args'][0];
            $list = $args->getTrace();
            foreach ($list as $k => $v) {
                $t = isset($v['class']) ? $v['class'] . ':' : '';
                $t .= isset($v['function']) ? $v['function'] . '()' : '';
                $t .= isset($v['file']) ? ' [' . $v['file'] . ', ' . $v['line'] . ']' : '';
                $list[$k] = $t;
            }
            return implode(PHP_EOL, $list);
        } elseif ($trace['function'] == 'errorHandler') {
            ob_start();
            debug_print_backtrace();
            $traces = preg_split('/[\r\n]/i', htmlentities(ob_get_clean()));
            foreach ($traces as $k => $trace) {
                preg_match('/^#(\d+)  /', $trace, $matches);
                if (!isset($matches[1])) {
                    unset($traces[$k]);
                } else {
                    if (strlen($traces[$k]) > 200) {
                        $traces[$k] = substr($traces[$k], 0, 200) . ' ...';
                    }
                }
            }
            return implode(PHP_EOL, $traces);
        } elseif ($trace['function'] == 'fatalHandler') {
            return '';
        }
    }

}
